package com.jnshu.tools.wechat;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.jnshu.dto.person.*;
import lombok.extern.java.Log;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author Martin
 * @date 2019/8/6 20:37
 * 微信接口工具类
 */
@Log
public class WeChatUtil {
    //基础支持的access_token
    private static final String WE_CHAT_BASE_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    //通过code换取网页授权的access_token
    private static final String WE_CHAT_CODE_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
    private static final String APP_ID = "wx0b31bcd6cbe880a4";
    private static final String APP_SECRET = "ef768d40a279dc0c811e6fda9dbbe176";
    //获取refresh
    private static final String WE_CHAT_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";
    //获取api_ticket
    private static final String SIGN_TICKET_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    //拉取用户信息
    private static final String USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";

    /**
     * 发起https请求并获取结果
     * @param requestUrl 请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr 提交的数据
     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
     */
    public static JSONObject httpRequest(String requestUrl,String requestMethod,String outputStr){
        JSONObject jsonObject = null;
        StringBuffer stringBuffer = new StringBuffer();
        try {
            // 创建SSLContext对象(SSLContext: 此类的实例表示安全套接字协议的实现)，并使用我们指定的信任管理器初始化
            TrustManager[] trustManagers = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL","SunJSSE");
            sslContext.init(null,trustManagers,new SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            httpsURLConnection.setSSLSocketFactory(sslSocketFactory);
            httpsURLConnection.setDoOutput(true);
            httpsURLConnection.setDoInput(true);
            httpsURLConnection.setUseCaches(false);
            // 设置请求方式（GET/POST）
            httpsURLConnection.setRequestMethod(requestMethod);
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr){
                OutputStream outputStream = httpsURLConnection.getOutputStream();
                // 注意编码格式，防止中文乱码
                outputStream.write(outputStr.getBytes(StandardCharsets.UTF_8));
                outputStream.close();
            }
            // 将返回的输入流转换成字符串
            InputStream inputStream = httpsURLConnection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream,StandardCharsets.UTF_8);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str;
            while ((str = bufferedReader.readLine()) != null){
                stringBuffer.append(str);
            }// 释放资源
            bufferedReader.close();
            inputStream.close();
            httpsURLConnection.disconnect();
            jsonObject = JSONObject.parseObject(stringBuffer.toString());
        }catch (ConnectException ce){
            log.severe("连接超时"+ce);
        }catch (Exception e){
            log.severe("https请求异常"+e);
        }
        return jsonObject;
    }

    /**
     * 获取基础支持access_token
     * @return WeChatAccessToken
     */
    public static WeChatBaseAccessToken getBaseAccessToken(){
        WeChatBaseAccessToken weChatBaseAccessToken = null;
        // 调用字符串的replace替换方法将url 中的appid,appsecret替换掉
        String requestUrl = WE_CHAT_BASE_TOKEN_URL.replace("APPID",APP_ID).replace("APPSECRET",APP_SECRET);
        // 调用通用的https请求方法
        JSONObject jsonObject = httpRequest(requestUrl,"GET",null);
        if (jsonObject == null || jsonObject.getString("errcode") != null){
            throw new JSONException("提醒: code码错误或已失效, 生成基础token失败!");
        }
        //请求成功
        try {
            weChatBaseAccessToken = new WeChatBaseAccessToken();
            weChatBaseAccessToken.setAccessToken(jsonObject.getString("access_token"));
            weChatBaseAccessToken.setExpiresIn(jsonObject.getIntValue("expires_in"));
        }catch (JSONException e){
            log.severe("获取token失败 errcode:{} errmsg:{}"+jsonObject.get("errcode")+jsonObject.getString("errmsg"));
        }
        return weChatBaseAccessToken;
    }

    /**
     * 获取网页授权token
     * @param code
     * @return WeChatTokenDto
     * 网页授权的access_token返回的数据
     */
    public static WeChatAccessToken getCodeAccessToken(String code){
        WeChatAccessToken weChatAccessToken = null;
        //调用字符串的replace替换方法将url 中的APPID,APPSECRET,CODE替换掉
        String requestUrl = WE_CHAT_CODE_TOKEN_URL.replace("APPID",APP_ID).replace("SECRET",APP_SECRET).replace("CODE",code);
        // 发起GET请求获取凭证
        JSONObject jsonObject = httpRequest(requestUrl,"GET",null);
        log.info("微信基本信息返回值："+jsonObject);
        //如果请求成功
        if (jsonObject != null){
            try {
                weChatAccessToken = new WeChatAccessToken();
                weChatAccessToken.setAccessToken(jsonObject.getString("access_token"));
                weChatAccessToken.setExpiresIn(jsonObject.getIntValue("expires_in"));
                weChatAccessToken.setOpenId(jsonObject.getString("openid"));
                weChatAccessToken.setRefreshToken(jsonObject.getString("refresh_token"));
                weChatAccessToken.setScope(jsonObject.getString("scope"));
            }catch (JSONException e){
                log.severe("获取accessToken失败，授权失败，无法获取微信用户信息 errcode:{} errmsg:{}"+jsonObject.get("errcode")+jsonObject.getString("errmsg"));
            }
        }
        return weChatAccessToken;
    }

    /**
     * 刷新网页授权token
     * @param refresh_token
     * @return WeChatToken
     * 网页授权的access_token返回的数据
     */
    public static WeChatRefreshToken getRefuseToken(String refresh_token){
        WeChatRefreshToken weChatRefreshToken = null;
        //调用字符串的replace替换方法,将url中的appid,appsecret,code替换掉
        String requestUrl = WE_CHAT_REFRESH_TOKEN_URL.replace("APPID",APP_ID).replace("REFRESH_TOKEN",refresh_token);
        // 发起GET请求获取凭证
        JSONObject jsonObject = httpRequest(requestUrl,"GET",null);
        log.info("微信基本信息返回值："+jsonObject);
        //如果请求成功
        if (jsonObject != null){
            try {
                weChatRefreshToken = new WeChatRefreshToken();
                weChatRefreshToken.setAccessToken(jsonObject.getString("access_token"));
                weChatRefreshToken.setExpiresIn(jsonObject.getIntValue("expires_in"));
                weChatRefreshToken.setOpenId(jsonObject.getString("openid"));
                weChatRefreshToken.setRefreshToken(jsonObject.getString("refresh_token"));
                weChatRefreshToken.setScope(jsonObject.getString("scope"));
            }catch (JSONException e){
                log.severe("获取accessToken失败，授权失败，无法获取微信用户信息 errcode:{} errmsg:{}"+jsonObject.get("errcode")+jsonObject.getString("errmsg"));
            }
        }
        return weChatRefreshToken;
    }

    /**
     * 获取jsApi_Ticket 临时票据
     * @param accessToken
     * @return WeChatJsApiTicket 对象
     */
    public static WeChatJsApiTicket getJsApiTicket(String accessToken){
        WeChatJsApiTicket weChatJsApiTicket = null;
        String requestUrl = SIGN_TICKET_CREATE_URL.replace("ACCESS_TOKEN",accessToken);
        JSONObject jsonObject = httpRequest(requestUrl,accessToken,null);
        if (jsonObject != null){
            weChatJsApiTicket = new WeChatJsApiTicket();
            try {
                weChatJsApiTicket.setTicket(jsonObject.getString("ticket"));
                weChatJsApiTicket.setExpiresIn(jsonObject.getIntValue("expires_in"));
            }catch (JSONException e){
                log.severe("获取jsApiTicket失败 errcode:{} errmsg:{}"+jsonObject.getIntValue("errMode")+jsonObject.getString("errmsg"));
            }
        }
        return weChatJsApiTicket;
    }

    /**
     * 生成随机字符串
     * @return String
     */
    private static String createNonceStr() {
        return UUID.randomUUID().toString();
    }

    /**
     * 用于生成签名的时间戳字符串
     * @return String
     */
    private static String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    /**
     * 进制转换方法
     * @return String
     */
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     *
     * @param js_api_ticket （用户临时凭证）
     * @param url （当前网页的URL，不包含#及其后面部分）
     * @return  Map (包含时间戳，随机字符串，签名等)
     */
    public static Map<String,String> getSign(String js_api_ticket,String url){
        log.info("当前网页的url是："+url);
        Map<String,String> request = new HashMap<>();
        String nonce_str = createNonceStr();
        String timestamp = createTimestamp();
        String signature = "";
        //注意这里参数名必须全部小写，且必须有序
        String string = "js_api_ticket="+js_api_ticket+
                        "&nonce_str="+nonce_str+
                        "&timestamp="+timestamp+
                        "&url="+url;
        try {
            MessageDigest crypt = null;
            crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            // 对string字符串进行SHA-1加密处理
            crypt.update(string.getBytes(StandardCharsets.UTF_8));
            // 对加密后字符串转成16进制
            signature = byteToHex(crypt.digest());
        }catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        request.put("url",url);
        request.put("js_api_ticket",js_api_ticket);
        request.put("nonce_str",nonce_str);
        request.put("timestamp",timestamp);
        request.put("signature",signature);
        return request;
    }

    /**
     * @param accessToken 网页授权token
     * @param openid  用户唯一标识
     * @return WeChatUserDto
     * 授权获取的微信用户的信息
     */
    public static WeChatUserDto getWeChatUserInfo(String accessToken,String openid){
        WeChatUserDto weChatUserDto = null;
        String url = USER_INFO_URL.replace("ACCESS_TOKEN",accessToken).replace("OPENID",openid);
        JSONObject jsonObject = httpRequest(url,"GET",null);
        if (jsonObject != null){
            try {
                weChatUserDto = new WeChatUserDto();
                weChatUserDto.setOpenid(jsonObject.getString("openid"));
                weChatUserDto.setNickname(jsonObject.getString("nickname"));
                weChatUserDto.setHeadImgUrl(jsonObject.getString("headimgurl"));
                // 用户的性别（1是男性，2是女性，0是未知）
                weChatUserDto.setSex(jsonObject.getIntValue("sex"));
                weChatUserDto.setCountry(jsonObject.getString("country"));
                weChatUserDto.setProvince(jsonObject.getString("province"));
                weChatUserDto.setCity(jsonObject.getString("city"));
            }catch (Exception e){
                int errorCode = jsonObject.getIntValue("errcode");
                String errorMsg = jsonObject.getString("errmsg");
                log.severe("获取用户信息失败 errcode:{} errmsg:{}"+errorCode+errorMsg);
            }
        }
        return weChatUserDto;
    }

    /**
     * 文件类型识别方法
     * @param contentType
     * @return String
     * */
    public static String getFileExt(String contentType) {
        String fileExt = "";
        if ("image/jpeg".equals(contentType)) {
            fileExt = ".jpg";
        } else if ("image/png".equals(contentType)) {
            fileExt = ".png";
        } else if ("image/gif".equals(contentType)) {
            fileExt = ".gif";
        } else if ("audio/mpeg".equals(contentType)) {
            fileExt = ".mp3";
        } else if ("audio/amr".equals(contentType)) {
            fileExt = ".amr";
        } else if ("video/mp4".equals(contentType)) {
            fileExt = ".mp4";
        } else if ("video/mpeg4".equals(contentType)) {
            fileExt = ".mp4";
        }
        return fileExt;
    }
}
